{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Design Assist: PLL Stability\n",
    "\n",
    "## Transfer function\n",
    "\n",
    "Let us consider the closed-loop PLL response, $H(s)$, built from the open-loop characteristics $G(s)$:\n",
    "$$ H(s)\\triangleq\\frac{\\varphi_{out}(s)}{\\varphi_{in}(s)}=\\frac{G(s)}{1+G(s)}$$\n",
    "\n",
    "where the open-loop response is given by:\n",
    "$$ G(s)=\\frac{K}{s}\\left(\\frac{1+s/\\omega_z}{1+s/\\omega_p}\\right).$$\n",
    "\n",
    "The 2nd order closed-loop response can therefore be expanded as:\n",
    "$$H(s)=K\\omega_p\\frac{1+s/\\omega_z}{s^2+s\\omega_p(1+K/\\omega_z) + K\\omega_p}.$$\n",
    "\n",
    "The above equation can also be re-written in standard form as:\n",
    "$$H(s)=\\omega_n^2\\frac{1+s(2\\zeta/\\omega_n)}{s^2+2\\zeta\\omega_n s + \\omega_n^2}$$\n",
    "where\n",
    "$$\\omega_n^2=K\\omega_p$$\n",
    "$$\\zeta=\\frac{1}{2}\\left(\\frac{\\omega_p}{\\omega_n}+\\frac{\\omega_n}{\\omega_z}\\right)$$\n",
    "\n",
    "## Step Error Response\n",
    "\n",
    "$$Y_{u,error}=\n",
    "\\frac{1}{2}\\left(\\zeta\\frac{2\\alpha-1}{\\sqrt{\\zeta^2-1}}+1\\right)e^{-\\omega_n t(\\zeta+\\sqrt{\\zeta^2-1})}\n",
    "-\\frac{1}{2}\\left(\\zeta\\frac{2\\alpha-1}{\\sqrt{\\zeta^2-1}}-1\\right)e^{-\\omega_n t(\\zeta-\\sqrt{\\zeta^2-1})}\n",
    "$$\n",
    "where\n",
    "$$\\alpha \\triangleq \\frac{\\omega_n}{2\\zeta K}$$\n",
    "\n",
    "## References\n",
    "\n",
    " 1. W. Egan, Phase-Lock Basics, 2nd ed., Wiley-Interscience, 2007, ISBN: 978-0-470-11800-9."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "include(\"PhaseLock.jl\")\n",
    "import PhaseLock: Xf1, Sys2\n",
    "import InspectDR\n",
    "using NumericIO\n",
    "\n",
    "#Convenience aliases:\n",
    "SI(x) = formatted(x, :SI, ndigits=3)\n",
    "\n",
    "#When MIME\"image/svg+xml\" is disabled, Jupyter eventually requests PNG inline graphics:\n",
    "#(SVG outputs do not render well in notebooks for some reason...)\n",
    "InspectDR.defaults.rendersvg = false\n",
    "\n",
    "nothing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Setup: Interact, Reactive, and Signals\n",
    "(Also create initial plot object)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "using Interact, Reactive\n",
    "\n",
    "#fopt = [(\"kHz\", 1.0e3), (\"MHz\", 1.0e6), (\"GHz\", 1.0e9)] #Make sure to use Float64\n",
    "#freqmap = Dict{Float64, String}(v=>s for (s,v) in fopt)\n",
    "#fmult = Signal(Float64, 1e9) #Frequency multiplier\n",
    "#annot = Signal(Bool, true) #Annotate plot\n",
    "fmin = Signal(Float64, 1e5) #Minimum plot frequency\n",
    "fmax = Signal(Float64, 1e9) #Maximum plot frequency\n",
    "tmax = Signal(Float64, 10e-6) #Maximum plot time\n",
    "ωnperiods = Signal(Float64, 10) #Maximum plot time (relative to ωn)\n",
    "G = Signal(PhaseLock.Xf1(K=10.0^(150/20), ωp=2pi*500e3, ωz=2pi*50e6)) #Open-loop characteristics\n",
    "pobj = PhaseLock.newplot() #Create initial plot object\n",
    "\n",
    "nothing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Interact/Reactive Control: Plot limits\n",
    "\n",
    "\n",
    "## TODO"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "#txt_fmax = textbox(value(fmax), label=\"max freq\", signal=fmax) #Will not display - notebook hangs??\n",
    "#display(txt_fmin) Will hang session using this method, for some reason\n",
    "txt_fmin = textbox(value(fmin), label=\"min freq\") #This one does not cause hangups\n",
    "fmin = signal(txt_fmin) #Hack to get fmax/txt_fmax working\n",
    "txt_fmax = textbox(value(fmax), label=\"max freq\")\n",
    "fmax = signal(txt_fmax)\n",
    "txt_tmax = textbox(value(tmax), label=\"max time\")\n",
    "tmax = signal(txt_tmax)\n",
    "txt_ωnperiods = textbox(value(ωnperiods), label=\"ωn periods\")\n",
    "ωnperiods = signal(txt_ωnperiods)\n",
    "map(display, [txt_fmin, txt_fmax, txt_tmax, txt_ωnperiods])\n",
    "nothing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Interact/Reactive Control: Inline Plots\n",
    "Use sliders/text boxes to change system parameters\n",
    "\n",
    "## TODO\n",
    "\n",
    "- Figure out why Interact does not update HTML correctly when using \\$LaTeX\\$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "#Gain slider:\n",
    "sld_KdB = slider(-10:1.0:400, value=20*log10(value(G).K), label=\"OL gain (dB)\")\n",
    "display(sld_KdB)\n",
    "\n",
    "#Pole/zero sliders (log(f) values):\n",
    "pzrange = 1:0.1:9\n",
    "sld_fp = slider(1:0.1:9, value=log10(value(G).ωp/(2pi)), label=\"log(pole)\")\n",
    "sld_fz = slider(1:0.1:9, value=log10(value(G).ωz/(2pi)), label=\"log(zero)\")\n",
    "map(display, [sld_fp, sld_fz])\n",
    "\n",
    "#Build widget list to control plot for future code blocks\n",
    "#TODO: Why do widgets have to be displayed in stages?\n",
    "#widgetlist = [tgl_fmult, chk_annot, txt_fmax, sld_G, sld_fz, sld_fp1, sld_fp2]\n",
    "\n",
    "#Compute open-loop characteristics\n",
    "G_calc = map(signal(sld_KdB), signal(sld_fp), signal(sld_fz)) do _KdB, lfp, lfz\n",
    "    push!(G, PhaseLock.Xf1(K=10.0^(_KdB/20), ωp=2pi*10^lfp, ωz=2pi*10^lfz))\n",
    "end\n",
    "\n",
    "info_poles = map(G) do _G\n",
    "    txt(x::String) = \"$x\" #\"\\\\textrm{$x}\" #Not currently using LaTeX\n",
    "    return HTML(txt(\"G (open-loop): \") *\n",
    "        \"K=\" * SI(20*log10(_G.K)) * txt(\" dB\") *\n",
    "        \", 𝑓p=\" * txt(SI(_G.ωp/(2pi)) * \"Hz\") *\n",
    "        \", 𝑓z=\" * txt(SI(_G.ωz/(2pi)) * \"Hz\") *\n",
    "        txt(\" (\") * \"𝑓0=\" * txt(SI(PhaseLock.ω_0(_G)/(2pi))) * \"Hz)\"\n",
    "    )\n",
    "end\n",
    "display(info_poles)\n",
    "\n",
    "info_sys = map(G) do _G\n",
    "    s = PhaseLock.Sys2(_G)\n",
    "    α = SI(s.α); ζ = SI(s.ζ); ωn = SI(s.ωn)\n",
    "    return HTML(\"H (closed-loop): α=$α, ζ=$ζ, <b>ωn=$(ωn)rad/s</b>\")\n",
    "end\n",
    "display(info_sys)\n",
    "\n",
    "#Widget-controlled plot:\n",
    "updater_plot = map(fmin, fmax, tmax, ωnperiods, G) do _fmin, _fmax, _tmax, _ωnperiods,_G\n",
    "    s = PhaseLock.Sys2(_G)\n",
    "    PhaseLock.update(pobj, _fmin, _fmax, _tmax, _ωnperiods, _G)\n",
    "end\n",
    "display(updater_plot)\n",
    "\n",
    "nothing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Interact/Reactive Control: Inspect/Gtk GUI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gtkgui = display(InspectDR.GtkDisplay(), pobj) #Display plot in Gtk GUI.\n",
    "\n",
    "#Refresh GUI when plot changes:\n",
    "updater_gtkgui = map((p)->InspectDR.refresh(gtkgui), updater_plot)\n",
    "\n",
    "#Re-display GUI controls, for convenience:\n",
    "#map(display, widgetlist)\n",
    "\n",
    "nothing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Demo complete!"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Julia 0.6.0",
   "language": "julia",
   "name": "julia-0.6"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "0.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
